package healthchecker

import (
	"context"
	"fmt"
	"github.com/BurntSushi/toml"
	"net"
	"time"
)

func (s *Reviewer) ishealth(se Server,ctx context.Context) {
	s.l.Lock()
	defer s.l.Unlock()
	le := len(s.CheckRecord) - 1
	if dieSer, ok := s.dieServers[se.Addr]; ok {
		if dieSer.index == le {
			//已存入死亡服务器组 并且为本周期不再做检查
			return
		}
	}
	timeout := time.Duration(time.Millisecond * time.Duration(s.config.Timeout))
	_, err := net.DialTimeout("tcp", se.Addr, timeout)
	m := s.CheckRecord[le][se.Addr]
	if err != nil {
		m.Fail++
		m.FailInfo = append(m.FailInfo, Failure{se.Addr, time.Now().UnixNano() / 1e6, err})
	} else {
		m.Success++
	}
	if m.Fail >= s.config.DeathThreshold {
		//失败次数达到阈值 并且不在死亡服务器组内  写入死亡组记录，并发送到通道
		if _, ok := s.dieServers[se.Addr]; !ok {
			se.index = le
			s.dieServers[se.Addr] = se
			s.Hresult <- BackChan{0, se.OriginalData}
		}
	}
	if m.Success >= s.config.ReviveThreshold {
		//成功次数达到阈值 发送复活信息到通道，并从死亡组删除
		if dieSer, ok := s.dieServers[se.Addr]; ok {
			if dieSer.index != le {
				delete(s.dieServers,se.Addr)
				s.Hresult <- BackChan{1, se.OriginalData}
			}
		}
	}

	s.CheckRecord[le][se.Addr] = m
}

//写入历史健康检查记录
func (s *Reviewer) writeRecord(data map[string]Record) {
	index := len(s.CheckRecord)
	if index >= 1000 {
		s.CheckRecord = s.CheckRecord[99:]
	}
	s.CheckRecord = append(s.CheckRecord, data)
}

//检查服务器是否存活
func (s *Reviewer) checker() {
	//每隔三秒执行进行一次周期检查
	for {
		if len(s.servers) == 0 {
			fmt.Println("未载入服务器信息 健康检查组件等待中")
			time.Sleep(time.Second * 5)
			continue
		}
		ctx, _ := context.WithTimeout(context.Background(), time.Second*time.Duration(s.config.Cycle))
		//写入新的周期记录
		s.l.Lock()
		s.writeRecord(map[string]Record{})
		s.l.Unlock()
		go s.task(ctx)
		<-ctx.Done()
	}
}

func (s *Reviewer) task(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			for _, v := range s.servers {
				go s.ishealth(v,ctx)
			}
			time.Sleep(time.Millisecond * time.Duration(s.config.Interval))
		}
	}
}

func init() {
	if _, err := toml.DecodeFile("./configs/healthchecker.toml", &setting); err != nil {
		panic("健康检查配置文件加载失败:")
	} else {
		H.config = setting.Config
		fmt.Println("健康检查配置文件加载成功")
	}
}

//重置参数信息
func (s *Reviewer) Reset(data interface{}) {
	s.l.Lock()
	defer s.l.Unlock()
	serversInfo := data.(Hinterface).GetServers()
	//初始化配置信息
	s.config = setting.Config
	//错误消息通道
	s.Hresult = make(chan BackChan, 1000)
	s.CheckRecord = []map[string]Record{}
	//载入上游服务器信息
	s.servers = serversInfo
	s.dieServers = map[string]Server{}
}

//启动
func Run() {
	if !H.config.State {
		fmt.Println("健康检查配置已关闭")
		return
	}
	//健康检查
	go H.checker()
}
